package com.ciccwm.common.auth.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁服务.
 * 基于Redis实现的分布式锁，支持获取锁、释放锁和执行带锁操作.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DistributedLockService {

    /**
     * Redis操作模板.
     */
    private final RedisTemplate<String, String> redisTemplate;

    /**
     * 分布式锁键前缀.
     */
    private static final String LOCK_PREFIX = "oauth:lock:";

    /**
     * 默认锁超时时间（秒）.
     */
    private static final long DEFAULT_TIMEOUT = 30;

    /**
     * 默认时间单位.
     */
    private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;

    /**
     * 获取锁的Lua脚本.
     * 使用SETNX命令设置锁，并设置过期时间.
     */
    private static final String LOCK_SCRIPT = 
        "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
        "redis.call('pexpire', KEYS[1], ARGV[2]) " +
        "return 1 " +
        "else " +
        "return 0 " +
        "end";

    /**
     * 释放锁的Lua脚本.
     * 检查锁的值是否匹配，匹配则删除锁.
     */
    private static final String UNLOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "return redis.call('del', KEYS[1]) " +
        "else " +
        "return 0 " +
        "end";

    /**
     * 获取锁的Redis脚本.
     */
    private final DefaultRedisScript<Long> lockScript = new DefaultRedisScript<>(LOCK_SCRIPT, Long.class);

    /**
     * 释放锁的Redis脚本.
     */
    private final DefaultRedisScript<Long> unlockScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);

    /**
     * 获取分布式锁
     *
     * @param key 锁的键
     * @return 锁的值，用于解锁
     */
    public String acquireLock(String key) {
        String lockKey = LOCK_PREFIX + key;
        String lockValue = UUID.randomUUID().toString();
        long timeoutMillis = DEFAULT_TIME_UNIT.toMillis(DEFAULT_TIMEOUT);

        try {
            Long result = redisTemplate.execute(
                lockScript,
                Collections.singletonList(lockKey),
                lockValue,
                String.valueOf(timeoutMillis)
            );

            if (result != null && result == 1) {
                return lockValue;
            }
        } catch (Exception e) {
            log.error("Failed to acquire lock for key: {}", key, e);
        }
        return null;
    }

    /**
     * 释放分布式锁
     *
     * @param key 锁的键
     * @param value 锁的值
     * @return 是否成功释放
     */
    public boolean releaseLock(String key, String value) {
        String lockKey = LOCK_PREFIX + key;
        try {
            Long result = redisTemplate.execute(
                unlockScript,
                Collections.singletonList(lockKey),
                value
            );
            return result != null && result == 1;
        } catch (Exception e) {
            log.error("Failed to release lock for key: {}", key, e);
            return false;
        }
    }

    /**
     * 使用分布式锁执行操作
     *
     * @param key 锁的键
     * @param operation 要执行的操作
     * @param <T> 返回值类型
     * @return 操作结果
     */
    public <T> T executeWithLock(String key, LockOperation<T> operation) {
        String lockValue = acquireLock(key);
        if (lockValue == null) {
            throw new RuntimeException("Failed to acquire lock for key: " + key);
        }

        try {
            return operation.execute();
        } finally {
            releaseLock(key, lockValue);
        }
    }

    /**
     * 分布式锁操作接口.
     *
     * @param <T> 操作返回值类型
     */
    @FunctionalInterface
    public interface LockOperation<T> {
        /**
         * 执行锁定的操作.
         *
         * @return 操作结果
         */
        T execute();
    }
} 
